﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using nwrole.plugin;
using nwrole.toolbox.sync.ServiceHost;
using nwrole.toolbox.sync.client;

namespace nwrole.toolbox.sync.SourcePlugin
{
    [Export(typeof(IPlugin))]
    public class SyncSourcePlugin : IPlugin
    {
        [NwroleConfig("SyncIntervalSeconds")]
        public int SyncIntervalSeconds { get; set; }

        [NwroleConfig("SrcDbConnectionString")]
        public string SrcDbConnectionString { get; set; }

        [NwroleConfig("SyncDbConnectionString")]
        public string SyncDbConnectionString { get; set; }

        [NwroleConfig("SyncInstanceId")]
        public int SyncInstanceId { get; set; }

        [NwroleConfig("SyncLockSeconds")]
        public int SyncLockSeconds { get; set; }

        // Source Service Connection Info

        [NwroleConfig("SyncSrcServiceConnType")]
        public string SyncSrcServiceConnType { get; set; }

        [NwroleConfig("SyncSrcServiceSBServiceNamespace")]
        public string SyncSrcServiceSBServiceNamespace { get; set; }

        [NwroleConfig("SyncSrcServiceSBServicePath")]
        public string SyncSrcServiceSBServicePath { get; set; }

        [NwroleConfig("SyncSrcServiceSBIssuerName")]
        public string SyncSrcServiceSBIssuerName { get; set; }

        [NwroleConfig("SyncSrcServiceSBIssuerSecret")]
        public string SyncSrcServiceSBIssuerSecret { get; set; }

        [NwroleConfig("SyncSrcServiceWSBaseAddress")]
        public string SyncSrcServiceWSBaseAddress { get; set; }

        [NwroleConfig("SyncSrcServiceWSAddress")]
        public string SyncSrcServiceWSAddress { get; set; }

        // Destination Service Connection Info

        [NwroleConfig("SyncDestServiceConnType")]
        public string SyncDestServiceConnType { get; set; }

        [NwroleConfig("SyncDestServiceSBServiceNamespace")]
        public string SyncDestServiceSBServiceNamespace { get; set; }

        [NwroleConfig("SyncDestServiceSBServicePath")]
        public string SyncDestServiceSBServicePath { get; set; }

        [NwroleConfig("SyncDestServiceSBIssuerName")]
        public string SyncDestServiceSBIssuerName { get; set; }

        [NwroleConfig("SyncDestServiceSBIssuerSecret")]
        public string SyncDestServiceSBIssuerSecret { get; set; }

        [NwroleConfig("SyncDestServiceWSBaseAddress")]
        public string SyncDestServiceWSBaseAddress { get; set; }

        [NwroleConfig("SyncDestServiceWSAddress")]
        public string SyncDestServiceWSAddress { get; set; }

        protected IPluginDebug debug;

        private bool running = false;

        private AutoResetEvent waitEvent = new AutoResetEvent(false);

        public SyncSourcePlugin()
        {
            SyncIntervalSeconds = 60;
        }

        private void WriteLog(string content) {
            Console.WriteLine(content);
            debug.Log(LogLevel.Debug, content, null);
            /*try {
                string logDir = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "log/TestPluginB/");
                if (!System.IO.Directory.Exists(logDir))
                    System.IO.Directory.CreateDirectory(logDir);

                DateTime now = DateTime.Now;

                System.IO.File.AppendAllText(logDir + "log " + now.ToString("yyyy-MM-dd") + ".txt", now.ToString("yyyy-MM-dd HH:mm:ss ") + content + "\r\n");
            } catch {
            }*/
        }

        public void Start(IPluginDebug debug) {
            this.debug = debug;

            WriteLog("SyncSourcePlugin starting");

            SyncSourceServiceHost srcHost = new SyncSourceServiceHost();
            srcHost.Start(SyncSourceConnInfo, SrcDbConnectionString, SyncDbConnectionString, SyncDestinationConnInfo, new TimeSpan(0,0,SyncLockSeconds));

            /*ServiceConnectionInfo localConnInfo = new ServiceConnectionInfo();
            localConnInfo.Type = ServiceConnectionInfo.ServiceConnectionType.WebService;
            localConnInfo.WebServiceBaseAddress = "localhost:8192/";
            localConnInfo.WebServiceAddress = "LocalService";
            SyncSourceServiceClient client = new SyncSourceServiceClient(localConnInfo);*/
            SyncSourceServiceClient client = new SyncSourceServiceClient(SyncSourceConnInfo);

            WriteLog("SyncSourcePlugin running");

            int curSyncIntervalSeconds = SyncIntervalSeconds;
            int retriesLevel = 0;
            int numRetries = 0;

            running = true;
            while(running)
            {
                debug.Pulse("Syncing");
                WriteLog(string.Format("SyncSourcePlugin syncing instance: {0} time: {1}", SyncInstanceId, DateTime.Now));
                //client.SyncAllSyncEntities();
                try
                {
                    if (client.StartFullSync(SyncInstanceId))
                    {
                        WriteLog(string.Format("StartFullSync start instance: {0} time: {1}", SyncInstanceId, DateTime.Now));
                        while (client.SyncNextEntity(SyncInstanceId))
                        {
                        }
                        WriteLog(string.Format("StartFullSync end instance: {0} time: {1}", SyncInstanceId, DateTime.Now));
                    }
                    retriesLevel = 0;
                    numRetries = 0;
                    curSyncIntervalSeconds = SyncIntervalSeconds;
                }
                catch(Exception e)
                {
                    Trace.TraceError("SyncSourcePlugin Error running full sync: " + e);
                    numRetries++;
                    if(numRetries % 10 == 0 && retriesLevel < 3)
                    {
                        retriesLevel++;
                        switch (retriesLevel)
                        {
                            case 1:
                                curSyncIntervalSeconds = SyncIntervalSeconds*5;
                                break;
                            case 2:
                                curSyncIntervalSeconds = SyncIntervalSeconds * 10;
                                break;
                        }
                        
                        Trace.TraceInformation("SyncSourcePlugin number of retries reached {0} retries level now {1} curSyncIntervalSec={2}", numRetries, retriesLevel, curSyncIntervalSeconds);

                        if (retriesLevel == 3)
                            throw;

                        numRetries = 0;
                    }
                }

                waitEvent.WaitOne(curSyncIntervalSeconds * 1000);
            }

            WriteLog("SyncSourcePlugin stopped");
        }

        public void Stop() {
            WriteLog("SyncSourcePlugin stopping");
            running = false;
            waitEvent.Set();
        } 

        public void ConfigurationChanged() {
            WriteLog("SyncSourcePlugin ConfigurationChanged()");
        }

        public ServiceConnectionInfo SyncSourceConnInfo
        {
            get
            {
                ServiceConnectionInfo connInfo = new ServiceConnectionInfo();

                ServiceConnectionInfo.ServiceConnectionType type;
                ServiceConnectionInfo.ServiceConnectionType.TryParse(SyncSrcServiceConnType, true, out type);
                connInfo.Type = type;

                connInfo.ServiceBusServiceNamespace = SyncSrcServiceSBServiceNamespace;
                connInfo.ServiceBusServicePath = SyncSrcServiceSBServicePath;
                connInfo.ServiceBusIssuerName = SyncSrcServiceSBIssuerName;
                connInfo.ServiceBusIssuerSecret = SyncSrcServiceSBIssuerSecret;

                connInfo.WebServiceBaseAddress = SyncSrcServiceWSBaseAddress;
                connInfo.WebServiceAddress = SyncSrcServiceWSAddress;

                return connInfo;
            }
        }

        public ServiceConnectionInfo SyncDestinationConnInfo
        {
            get
            {
                ServiceConnectionInfo connInfo = new ServiceConnectionInfo();

                ServiceConnectionInfo.ServiceConnectionType type;
                ServiceConnectionInfo.ServiceConnectionType.TryParse(SyncDestServiceConnType, true, out type);
                connInfo.Type = type;

                connInfo.ServiceBusServiceNamespace = SyncDestServiceSBServiceNamespace;
                connInfo.ServiceBusServicePath = SyncDestServiceSBServicePath;
                connInfo.ServiceBusIssuerName = SyncDestServiceSBIssuerName;
                connInfo.ServiceBusIssuerSecret = SyncDestServiceSBIssuerSecret;

                connInfo.WebServiceBaseAddress = SyncDestServiceWSBaseAddress;
                connInfo.WebServiceAddress = SyncDestServiceWSAddress;

                return connInfo;
            }
        }
    }
}
